/**
 * 
 */
package com.binary.redis;

import static org.springframework.util.Assert.notNull;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.util.CollectionUtils;

import com.binary.constant.RedisConstant;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.util.Pool;

/**
 * @author dbinary
 * @date 2018年7月26日
 * @function redis sentinel 注入
 */
public class RedisSentinelFactory implements InitializingBean,DisposableBean{
	private static Logger logger = LoggerFactory.getLogger(RedisSentinelFactory.class);
	private String sentinelNodes;
	private String masterName;
	private String masterPassWord;
	private String poolSize;
	private JedisPoolConfig jedisPoolConfig;
	private Map<Integer,Set<RedisNode>> sentinelMap= new HashMap<Integer,Set<RedisNode>>();
	private Map<Integer,Pool<Jedis>> sentinePool = new HashMap<Integer,Pool<Jedis>>();
	/**
	 * 注入数据到哨兵池
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		if(sentinelMap!=null&&sentinelMap.size()>0){
			for(Map.Entry<Integer,Set<RedisNode>> entry:sentinelMap.entrySet()){
				if(masterPassWord==null&&"".equals(masterPassWord)){
					sentinePool.put(entry.getKey(),
							new JedisSentinelPool(masterName,convertJedisSentinelSet(entry.getValue()),jedisPoolConfig));
					
				}else{
					sentinePool.put(entry.getKey(),
							new JedisSentinelPool(masterName,convertJedisSentinelSet(entry.getValue()),jedisPoolConfig,masterPassWord));
					
				}
			}
		}
		
		String[] keys ={RedisConstant.RoleKey_.concat("*"),RedisConstant.UserKey_.concat("*")};
		sentinePool = getSentinePool();
		if(sentinePool!=null&&sentinePool.size()>0){
			for(int i=0;i<sentinePool.size();i++){
				Pool<Jedis> pool = sentinePool.get(i);
				for(String key:keys){
					Jedis jedis = pool.getResource();
					Set<String> jedisKeys = jedis.keys(key);
					if(jedisKeys!=null&&jedisKeys.size()>0){
						for(String jedisKey:jedisKeys){
							
							jedis.del(jedisKey);
							logger.info("删除redis key:"+jedisKey);
						}
					}
					
				}
			}
		}
		
		
		
	
	}
	
	
	/**
	 * @param value
	 * @return
	 */
	private static Set<String> convertJedisSentinelSet(Set<RedisNode> value) {
		
		if(CollectionUtils.isEmpty(value)){
			return Collections.emptySet();
		}
		Set<String> sets = new HashSet<String>(value.size());
		for(RedisNode node:value){
			if(node!=null){
				sets.add(node.asString());
			}
		}
		return sets;
	}
	
	public Pool<Jedis> getJedis(int key){
		notNull(key,"key is null");
		int index = key%Integer.parseInt(poolSize);
		Pool<Jedis> pool = getSentinelPool(index);
		return pool;
	}
	public Pool<Jedis> getJedis(String key){
		notNull(key,"key is null");
		int index = BKDRHash(key)%Integer.parseInt(poolSize);
		Pool<Jedis> pool = getSentinelPool(index);
		return pool;
	}
	public static int BKDRHash(String str) {
        int seed = 131;
        int hash = 0;

        for (int i = 0; i < str.length(); i++) {
            hash = (hash * seed) + str.charAt(i);
        }

        return (hash & 0x7FFFFFFF);
    }

	/**
	 * @param index
	 * @return
	 */
	private Pool<Jedis> getSentinelPool(int index) {
		Pool<Jedis> pool = sentinePool.get(index);
		if(pool==null||pool.isClosed()){
			if(masterPassWord==null||masterPassWord.equals("")){
				pool = new JedisSentinelPool(masterName,convertJedisSentinelSet(sentinelMap.get(index)),jedisPoolConfig);
				
			}
			else{
				pool = new JedisSentinelPool(masterName,convertJedisSentinelSet(sentinelMap.get(index)),jedisPoolConfig,masterPassWord);
			}
		}
		return pool;
	}


	public Map<Integer, Set<RedisNode>> getSentinelMap() {
		return sentinelMap;
	}


	public void setSentinelMap(Map<Integer, Set<RedisNode>> sentinelMap) {
		this.sentinelMap = sentinelMap;
	}


	public Map<Integer, Pool<Jedis>> getSentinePool() {
		return sentinePool;
	}


	public void setSentinePool(Map<Integer, Pool<Jedis>> sentinePool) {
		this.sentinePool = sentinePool;
	}


	public String getSentinelNodes() {
		return sentinelNodes;
	}

	/**
	 * covert ，；to node
	 * @param sentinelNodes
	 */
	public void setSentinelNodes(String sentinelNodes) {
		
		notNull(sentinelNodes,"redis.data.sentinels.SentinelNodes not set value is null");
		String[] sentinels =sentinelNodes.split(";");
		if(sentinels!=null&&sentinels.length>0){
		Map<Integer,Set<RedisNode>> sentinelMaps= new HashMap<Integer,Set<RedisNode>>();

			for(int i=0;i<sentinels.length;i++){
				String[] sentineNodes = sentinels[i].split(",");
				Set<RedisNode> redisSet = new HashSet<RedisNode>();
				if(sentineNodes!=null&&sentineNodes.length>0){
					for(int k = 0;k < sentineNodes.length; k++){
						String[] splits = sentineNodes[k].split(":");
						RedisNode node = new RedisNode(splits[0],Integer.parseInt(splits[1]));
						redisSet.add(node);
					}
					sentinelMaps.put(i, redisSet);
				}
				
				if(!sentinelMaps.isEmpty()){
					this.setSentinelMap(sentinelMaps);
				}
				
			}
		}
		
		this.sentinelNodes = sentinelNodes;
	}
	public String getMasterName() {
		return masterName;
	}


	public void setMasterName(String masterName) {
		this.masterName = masterName;
	}


	public String getMasterPassWord() {
		return masterPassWord;
	}


	public void setMasterPassWord(String masterPassWord) {
		this.masterPassWord = masterPassWord;
	}


	public String getPoolSize() {
		return poolSize;
	}


	public void setPoolSize(String poolSize) {
		this.poolSize = poolSize;
	}


	public JedisPoolConfig getJedisPoolConfig() {
		return jedisPoolConfig;
	}
	public void setJedisPoolConfig(JedisPoolConfig jedisPoolConfig) {
		this.jedisPoolConfig = jedisPoolConfig;
	}

	@Override
	public void destroy() throws Exception {
		if(sentinePool != null && !sentinePool.isEmpty()){
			for (Map.Entry<Integer, Pool<Jedis>> element : sentinePool.entrySet()) {
				Pool<Jedis> pool = element.getValue();
				pool.destroy();
			}
		}
	}
}
